
package SSF.OS;

import SSF.OS.TCP.*;
import SSF.OS.UDP.*;
import com.renesys.raceway.SSF.*;
import java.io.*;

/**
 * Write to the named OutputStream the log of packets crossing the NIC
 * in the binary tcpdump format.
 */
public class binaryTcpDump extends tcpDump {

  public void dump(PacketEvent pe) {
    rawPacket(pe,owner.now());
  }
  public void drop(PacketEvent pe) {
    // nothing?
  }

  private OutputStream outstream;

  public binaryTcpDump(OutputStream ostr,Entity ownedBy) {
    super(new PrintWriter(ostr),ownedBy);
    outstream = ostr;
  }

/** Write raw packet bytes to the output stream. */
  private void rawPacket(PacketEvent pkt, long atTime) {
    ProtocolMessage msg = pkt.getHeaders(); 

    double fsec = atTime*1./SSF.Net.Net.frequency;
    int sec = (int)fsec;
    int usec = (int)((fsec-sec)*1000000.);

    synchronized(this) {
      if (rbuf == null) {
	rbuf = new byte[4096];
	write_raw_file_header();
      }
    }

    if (msg instanceof IpHeader) {

      IpHeader iphdr = (IpHeader)msg;

      switch (iphdr.PROTOCOL_NO) {
      case Protocols.TCP_PRTL_NUM:

	/** I.  Write the four-word per-packet tcpdump header. */

	rawh(4,sec);               // timestamp: seconds
	rawh(4,usec);              // timestamp: microseconds
	rawh(4,44);                // length of portion present: 11 words 
	rawh(4,1500);              // length of packet (off wire)
	
	/** II. Write the "null" link-level encapsulation header (1 word) */
	
	raw(4,0);
	/** III.  Write the IP packet header: 5 words. */
	
	
	raw(1,(4<<4) + 5);         // IP version (4) and header length (5w)
	//raw(1,0);                  // Type of Service
	raw(1,iphdr.TOS);          // Type of Service
	raw(2,iphdr.bytecount());  // packet octet count
	raw(2,0);                  // datagram id
	raw(2,0);                  // flags and fragment offset
	raw(1,iphdr.TIME_TO_LIVE); // time to live
	raw(1,iphdr.PROTOCOL_NO);  // IP protocol
	raw(2,0);                  // header checksum
	raw(4,iphdr.SOURCE_IP);    // source IP address
	raw(4,iphdr.DEST_IP);      // destination IP address
	
	/** IV.  Write the TCP packet header: 5 words. */
	
	TCP_Message tcpHdr = (TCP_Message)iphdr.payload();
	
	raw(2,tcpHdr.SOURCE_port);   // source port 
	raw(2,tcpHdr.DEST_port);     // destination port
	raw(4,tcpHdr.SEQno);         // sequence number
	raw(4,tcpHdr.ACKno);         // acknowledgement number 
	raw(1,0x05 << 4);            // offset byte
        raw(1,tcpHdr.flags);         // flags
        raw(2,tcpHdr.AdvertisedWnd); // advertised window size
        raw(2,0);                    // checksum
        raw(2,0);                    // urgent offset 
	break;
	
      case Protocols.UDP_PRTL_NUM:

	/** I.  Write the four-word per-packet tcpdump header. */
	
	rawh(4,sec);               // timestamp: seconds
	rawh(4,usec);              // timestamp: microseconds
	rawh(4,32);                // length of portion present: 11 words 
	rawh(4,1500);              // length of packet (off wire)
	
	/** II. Write the "null" link-level encapsulation header (1 word) */
	
	raw(4,0);

	/** III.  Write the IP packet header: 5 words. */
	
	
	raw(1,(4<<4) + 5);         // IP version (4) and header length (5w)
	//raw(1,0);                  // Type of Service
	raw(1,iphdr.TOS);          // Type of Service
	raw(2,iphdr.bytecount());  // packet octet count
	raw(2,0);                  // datagram id
	raw(2,0);                  // flags and fragment offset
	raw(1,iphdr.TIME_TO_LIVE); // time to live
	raw(1,iphdr.PROTOCOL_NO);  // IP protocol
	raw(2,0);                  // header checksum
	raw(4,iphdr.SOURCE_IP);    // source IP address
	raw(4,iphdr.DEST_IP);      // destination IP address
	
	
	UDP_Header udpHdr = (UDP_Header)iphdr.payload();
	raw(2,udpHdr.SOURCE_port); // source port
	raw(2,udpHdr.DEST_port);   // destination port
	raw(2,udpHdr.bytecount());      // udp packet length
	raw(2,0);                  // checksum
	break;
      default:
	System.err.println("TCPDUMP: Cannot dump non-TCP packet");
	break;
      }
    } else {
      System.err.println("TCPDUMP: Cannot dump non-IP packet");
    }
    raw_commit();
  }

  private byte[] rbuf;
  private int rct;

/** Write 1, 2, or 4 bytes to buffer in network byte order */
  private void raw(int len, long val) {
    switch(len) {
    case 1:
      rbuf[rct++] = (byte)(val);
      break;
    case 2:
      rbuf[rct++] = (byte)(val>>8);
      rbuf[rct++] = (byte)(val);
      break;
    case 4:
      rbuf[rct++] = (byte)(val >>24);
      rbuf[rct++] = (byte)(val >>16);
      rbuf[rct++] = (byte)(val >>8);
      rbuf[rct++] = (byte)(val );
      break;
    default:
      System.err.println("TCPDUMP: Error: raw write of bad byte count");
    }
  }

/** Write 1, 2, or 4 bytes to buffer in host order */
  private void rawh(int len, long val) {
    switch(len) {
    case 1:
      rbuf[rct++] = (byte)(val);
      break;
    case 2:
      rbuf[rct++] = (byte)(val);
      rbuf[rct++] = (byte)(val>>8);
      break;
    case 4:
      rbuf[rct++] = (byte)(val );
      rbuf[rct++] = (byte)(val >>8);
      rbuf[rct++] = (byte)(val >>16);
      rbuf[rct++] = (byte)(val >>24);
      break;
    default:
      System.err.println("TCPDUMP: Error: raw write of bad byte count");
    }
  }

/** Write buffered bytes to the printstream */
  private void raw_commit() {
    try {
      outstream.write(rbuf,0,rct);
      outstream.flush();
    } catch (Exception e) {
      System.err.println(e);
      System.exit(-1);
    }
    rct =0;
  }

/** Write the generic file header for TCP binary dump files */
  private void write_raw_file_header() {
    final int MAGIC = 0xA1B2C3D4; 
    final int MAJOR = 2, MINOR = 4; // making it up as we go along
    final int ZONE = 0, SIGFIGS = 0, SNAPLEN = 1500;
    final int LINKTYPE = 0;  // Ethernet encapsulation 
    
    rct = 0;
    rawh(4,MAGIC);           // magic number for tcpdump files
    rawh(2,MAJOR);           // major version number
    rawh(2,MINOR);           // minor version number
    rawh(4,ZONE);            // gmt to local correction
    rawh(4,SIGFIGS);         // accuracy of timestamps
    rawh(4,SNAPLEN);         // max length saved portion of each pkt
    rawh(4,LINKTYPE);        // data link type
    raw_commit();
  }
}

/*=                                                                      =*/
/*=  Copyright (c) 1997--2000  SSF Research Network                      =*/
/*=                                                                      =*/
/*=  SSFNet is open source software, distributed under the GNU General   =*/
/*=  Public License.  See the file COPYING in the 'doc' subdirectory of  =*/
/*=  the SSFNet distribution, or http://www.fsf.org/copyleft/gpl.html    =*/
/*=                                                                      =*/
